using System.Collections;
using DG.Tweening;
using Game.Scripts.Service;
using MoreMountains.Feedbacks;
using Sirenix.OdinInspector;
using UniRx;
using UnityEngine;
using Zenject;

namespace Game.Scripts.Entity
{
    public enum IkunBusState
    {
        Stationary,
        Moving
    }

    public enum IkunBusPosition
    {
        Left,
        Middle,
        Right
    }

    public enum IkunBusOperation
    {
        MoveLeft,
        MoveRight
    }


    public class IkunBus : MonoBehaviour
    {
        private GameService _gameService;
        private readonly ReactiveProperty<IkunBusState> currentState = new ReactiveProperty<IkunBusState>();
        private readonly ReactiveProperty<IkunBusPosition> currentPosition = new ReactiveProperty<IkunBusPosition>();
        [ReadOnly] public ReactiveProperty<int> playerMoveSpeed;
        [SerializeField] private int playerInitialSpeed = 25;
        [Range(0, 5)] [SerializeField] private float changePositionOffset;
        [Range(0, 100)] [SerializeField] private int speedGainPerBoost;
        [Range(0, 3f)] [SerializeField] private float changePositionDurationDefault;
        [Range(0, 3f)] [SerializeField] private float changePositionDurationAtBoost;
        [SerializeField] private int highSpeedThreshold;
        [SerializeField] private int speedMaxCap;
        private float changePositionDuration;
        [SerializeField] private Renderer fuseRenderer;
        private Vector3 originalPosition;
        private Quaternion originalRotation;
        [SerializeField] private Animator animator;
        [Header("Feedbacks")] [SerializeField] private MMF_Player collectFeedback;
        [SerializeField] private MMF_Player movingFeedback;
        [SerializeField] private MMF_Player idleFeedback;
        [SerializeField] private MMF_Player fuseBurningFeedback;
        [SerializeField] private MMF_Player explodeFeedback;
        [SerializeField] private MMF_Player clockTickingFeedback;
        [SerializeField] private MMF_Player speedBoostFeedback;
        [Header("Debug")] [ReadOnly] public ReactiveProperty<int> playerPoints;
        [ReadOnly] public int numberOfTilePassed;
        private static readonly int IsFuseActive = Shader.PropertyToID("_IsFuseActive");
        [ReadOnly] [SerializeField] private float _timeLimit;
        private Coroutine timberCo;
        private Coroutine fuseBurningCo;
        private static readonly int Transparency = Shader.PropertyToID("_Transparency");
        private static readonly int BlowupTrigger = Animator.StringToHash("BlowupTrigger");
        private PlayerInputService _playerInputService;

        [Inject]
        public void Construct(GameService gameService, PlayerInputService playerInputService)
        {
            _gameService = gameService;
            _playerInputService = playerInputService;
        }


        private void Awake()
        {
            SetBusDefaultState();
            originalPosition = transform.position;
            originalRotation = transform.rotation;
        }

        private void SetBusDefaultState()
        {
            currentState.Value = IkunBusState.Stationary;
            currentPosition.Value = IkunBusPosition.Middle;
            playerPoints.Value = 0;
            _gameService.playerScore.text = "0";
            playerMoveSpeed.Value = playerInitialSpeed;
            numberOfTilePassed = 0;
            fuseRenderer.material.SetFloat(Transparency, 1f);
        }

        private void Start()
        {
            playerMoveSpeed.Subscribe(next =>
            {
                if (next > highSpeedThreshold)
                {
                    changePositionDuration = changePositionDurationAtBoost;
                }
                else
                {
                    changePositionDuration = changePositionDurationDefault;
                }
            });
            currentPosition.Subscribe(next =>
            {
                if (next == IkunBusPosition.Middle)
                {
                    transform.DOMoveX(0, changePositionDuration);
                }
                else if (next == IkunBusPosition.Right)
                {
                    transform.DOMoveX(changePositionOffset, changePositionDuration);
                }
                else if (next == IkunBusPosition.Left)
                {
                    transform.DOMoveX(-changePositionOffset, changePositionDuration);
                }
            });
            playerPoints.Subscribe(next => { _gameService.playerScore.text = next.ToString(); });
            currentState.Subscribe(next =>
            {
                if (next == IkunBusState.Moving)
                {
                    movingFeedback.PlayFeedbacks();
                }
            });
        }

        [Button(ButtonSizes.Small)]
        private void TestBombExplode()
        {
            HandleBombExplode();
        }


        public void SetIkunBusState(IkunBusState state)
        {
            currentState.Value = state;
        }

        public void SetIkunBusPosition(IkunBusOperation busOperation)
        {
            if (currentPosition.Value == IkunBusPosition.Middle)
            {
                if (busOperation == IkunBusOperation.MoveLeft)
                {
                    currentPosition.Value = IkunBusPosition.Left;
                }
                else
                {
                    currentPosition.Value = IkunBusPosition.Right;
                }
            }
            else if (currentPosition.Value == IkunBusPosition.Left)
            {
                if (busOperation == IkunBusOperation.MoveRight)
                {
                    currentPosition.Value = IkunBusPosition.Middle;
                }
            }
            else if (currentPosition.Value == IkunBusPosition.Right)
            {
                if (busOperation == IkunBusOperation.MoveLeft)
                {
                    currentPosition.Value = IkunBusPosition.Middle;
                }
            }
        }


        private void Update()
        {
            if (currentState.Value == IkunBusState.Moving)
            {
                transform.Translate(playerMoveSpeed.Value * Time.deltaTime * Vector3.forward, Space.World);
            }
        }


        public void HandlePointCollect()
        {
            playerPoints.Value += 1;
            collectFeedback.PlayFeedbacks();
        }

        public void HandleHealthGain()
        {
            int targetValue = _gameService.playerHealth.Value + 1;

            if (targetValue > _gameService.playerMaxHealth)
            {
                targetValue = _gameService.playerMaxHealth;
            }

            _gameService.playerHealth.Value = targetValue;
        }

        public void HandleSpeedBoost()
        {
            int targetValue = playerMoveSpeed.Value + speedGainPerBoost;
            if (targetValue > speedMaxCap)
            {
                targetValue = speedMaxCap;
            }

            playerMoveSpeed.Value = targetValue;
            speedBoostFeedback.PlayFeedbacks();
        }

        public bool IfAtMaxSpeed()
        {
            return playerMoveSpeed.Value == speedMaxCap;
        }

        public bool IfAtMaxHealth()
        {
            return _gameService.playerHealth.Value == _gameService.playerMaxHealth;
        }


        public void PauseBusState()
        {
            idleFeedback.PlayFeedbacks();
            currentState.Value = IkunBusState.Stationary;
            StopCountDown();
        }

        public void ResetBusState()
        {
            idleFeedback.PlayFeedbacks();
            SetBusDefaultState();
            transform.position = originalPosition;
            transform.rotation = originalRotation;
        }


        public void HandleBombTimerConfig(float timeLimit)
        {
            if (timeLimit == -1)
            {
                // not using timber for this game level
                ToggleFuseState(false);
            }
            else
            {
                ToggleFuseState(true);
                _timeLimit = timeLimit;
                timberCo = StartCoroutine(Countdown(timeLimit));
                fuseBurningCo = StartCoroutine(FuseBurn(timeLimit));
            }
        }


        private void ToggleFuseState(bool state)
        {
            if (state)
            {
                fuseRenderer.material.SetInt(IsFuseActive, 1);
            }
            else
            {
                fuseRenderer.material.SetInt(IsFuseActive, 0);
            }
        }


        private IEnumerator Countdown(float timeLimit)
        {
            float copy = timeLimit;
            yield return new WaitUntil(() => currentState.Value == IkunBusState.Moving);
            while (copy >= 0)
            {
                yield return new WaitForSecondsRealtime(1f);
                copy--;
                if (copy <= 4)
                {
                    clockTickingFeedback.PlayFeedbacks();
                }
            }

            HandleBombExplode();
        }


        private IEnumerator FuseBurn(float timeLimit)
        {
            float copy = timeLimit;
            yield return new WaitUntil(() => currentState.Value == IkunBusState.Moving);
            fuseBurningFeedback.PlayFeedbacks();
            while (copy > 0)
            {
                float burnPercentage = 1 - (_timeLimit - copy) / _timeLimit;
                fuseRenderer.material.SetFloat(Transparency, burnPercentage);
                yield return new WaitForSecondsRealtime(0.01f);
                copy -= 0.01f;
            }
        }


        private void HandleBombExplode()
        {
            _playerInputService.ifListenInput = false;
            currentState.Value = IkunBusState.Stationary;
            animator.SetTrigger(BlowupTrigger);
            explodeFeedback.PlayFeedbacks();
        }


        public void HandleExplosionEnd()
        {
            _playerInputService.ifListenInput = true;
            _gameService.HandleGameRestart();
        }


        public void StopCountDown()
        {
            ToggleFuseState(false);
            if (timberCo != null)
            {
                StopCoroutine(timberCo);
            }

            if (fuseBurningCo != null)
            {
                StopCoroutine(fuseBurningCo);
            }
        }
    }
}